﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Linq.SqlClient;
using System.Collections.Generic;
using System.Reflection;


namespace Entity
{
    public static class QueryBuilder
    {
        public static IQueryBuilder<T> Create<T>()
        {
            return new QueryBuilder<T>();
        }

    }

    class QueryBuilder<T> : IQueryBuilder<T>
    {
        private Expression<Func<T, bool>> predicate;
        Expression<Func<T, bool>> IQueryBuilder<T>.Expression
        {
            get
            {
                return predicate;
            }
            set
            {
                predicate = value;
            }
        }

        public QueryBuilder()
        {
            predicate = PredicateBuilder.True<T>();
        }
    }

    /// <summary>
    /// 动态查询条件创建者
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IQueryBuilder<T>
    {
        Expression<Func<T, bool>> Expression { get; set; }
    }

    public static class IQueryBuilderExtensions
    {
        /// <summary>
        /// 建立 Between 查询条件
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="from">开始值</param>
        /// <param name="to">结束值</param>
        /// <returns></returns>
        public static IQueryBuilder<T> Between<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P from, P to)
        {
            var parameter = property.GetParameters();
            var constantFrom = Expression.Constant(from);
            var constantTo = Expression.Constant(to);
            Type type = typeof(P);
            Expression nonNullProperty = property.Body;
            //如果是Nullable<X>类型，则转化成X类型
            if (IsNullableType(type))
            {
                type = GetNonNullableType(type);
                nonNullProperty = Expression.Convert(property.Body, type);
            }
                var c1 = Expression.GreaterThanOrEqual(nonNullProperty, constantFrom);

                var c2 = Expression.LessThanOrEqual(nonNullProperty, constantTo);

                var c = Expression.AndAlso(c1, c2);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(c, parameter);

                q.Expression = q.Expression.And(lambda);
            return q;
        }


        /// <summary>
        /// 建立 Between 查询条件,允许from/to为null
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="from">开始值</param>
        /// <param name="to">结束值</param>
        /// <returns></returns>
        public static IQueryBuilder<T> BetweenFor<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P from, P to)
        {
            if (from != null && to != null)
            {
                return Between<T, P>(q, property, from, to);
            }
            else
            {
                var parameter = property.GetParameters();
                var constantFrom = Expression.Constant(from);
                var constantTo = Expression.Constant(to);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                if (from != null)
                {
                    var c1 = Expression.GreaterThanOrEqual(nonNullProperty, constantFrom);
                    Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(c1,parameter);
                    q.Expression = q.Expression.And(lambda);
                }
                else
                {
                    if (to != null)
                    {
                        var c2 = Expression.LessThanOrEqual(nonNullProperty, constantTo);
                        Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(c2, parameter);
                        q.Expression = q.Expression.And(lambda);
                    }
                }
            }
            return q;
        }

        /// <summary>
        /// 建立 Like ( 模糊 ) 查询条件
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="value">查询值</param>
        /// <returns></returns>
        public static IQueryBuilder<T> Like<T>(this IQueryBuilder<T> q, Expression<Func<T, string>> property, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                value = value.Trim();
                var parameter = property.GetParameters();
                var constant = Expression.Constant("%" + value + "%");
                MethodCallExpression methodExp = Expression.Call(null, typeof(SqlMethods).GetMethod("Like",
                    new Type[] { typeof(string), typeof(string) }), property.Body, constant);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);

                q.Expression = q.Expression.And(lambda);
            }
            return q;
        }


        /// <summary>
        /// or后面的条件是like
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="q"></param>
        /// <param name="property"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static IQueryBuilder<T> Or<T>(this IQueryBuilder<T> q, Expression<Func<T, string>> property, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                value = value.Trim();
                var parameter = property.GetParameters();
                var constant = Expression.Constant("%" + value + "%");
                MethodCallExpression methodExp = Expression.Call(null, typeof(SqlMethods).GetMethod("Like",
                    new Type[] { typeof(string), typeof(string) }), property.Body, constant);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);

                q.Expression = q.Expression.Or(lambda);
                //q.Expression = q.Expression.And(lambda);
            }
            return q;
        }
        /// <summary>
        /// Or后面的条件是 equal
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="P"></typeparam>
        /// <param name="q"></param>
        /// <param name="property"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static IQueryBuilder<T> OrEqual<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P value)
        {
            if (value != null)
            {
                var parameter = property.GetParameters();
                var constant = Expression.Constant(value);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                //如果是Nullable<X>类型，则转化成X类型
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                var methodExp = Expression.Equal(nonNullProperty, constant);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);
                q.Expression = q.Expression.Or(lambda);
            }
            return q;
        }

        /// <summary>
        /// 建立 Equals ( 相等 ) 查询条件
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="value">查询值</param>
        /// <returns></returns>
        public static IQueryBuilder<T> Equals<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P value)
        {
            if (value != null)
            {
                var parameter = property.GetParameters();
                var constant = Expression.Constant(value);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                //如果是Nullable<X>类型，则转化成X类型
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                var methodExp = Expression.Equal(nonNullProperty, constant);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);
                q.Expression = q.Expression.And(lambda);
            }
            return q;
        }
        /// <summary>
        /// 把两个expression链接起来用or表达
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="P"></typeparam>
        /// <param name="q"></param>
        /// <param name="exp1"></param>
        /// <param name="exp2"></param>
        /// <returns></returns>
        public static IQueryBuilder<T> OrFor<T>(this IQueryBuilder<T> q, IQueryBuilder<T> exp1)
        {
            if (exp1 != null)
            {

                q.Expression = q.Expression.Or(exp1.Expression);
            }
            return q;
        }

        /// <summary>
        /// 把两个expression链接起来用And表达
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="P"></typeparam>
        /// <param name="q"></param>
        /// <param name="exp1"></param>
        /// <param name="exp2"></param>
        /// <returns></returns>
        public static IQueryBuilder<T> EqualsFor<T>(this IQueryBuilder<T> q, IQueryBuilder<T> exp1)
        {
            if (exp1 != null)
            {

                q.Expression = q.Expression.And(exp1.Expression);
            }
            return q;
        }

        /// <summary>
        /// 建立 notEquals ( 相等 ) 查询条件
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="value">查询值</param>
        /// <returns></returns>
        public static IQueryBuilder<T> NotEquals<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P value)
        {
            if (value != null)
            {
                var parameter = property.GetParameters();
                var constant = Expression.Constant(value);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                //如果是Nullable<X>类型，则转化成X类型
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                var methodExp =Expression.NotEqual(nonNullProperty, constant);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);
                q.Expression = q.Expression.And(lambda);
            }
            return q;
        }

        /// <summary>
        /// 建立 小于数值 lessthan比较的查询条件
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="value">查询值</param>
        /// <returns></returns>
        public static IQueryBuilder<T> LessThan<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P value)
        {
            if (value != null)
            {
                var parameter = property.GetParameters();
                var constant = Expression.Constant(value);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                //如果是Nullable<X>类型，则转化成X类型
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                var methodExp =Expression.LessThan(nonNullProperty, constant);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);
                q.Expression = q.Expression.And(lambda);
            }
            return q;
        }


        /// <summary>
        /// 建立 大于数值 greaterthan比较的查询条件
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="value">查询值</param>
        /// <returns></returns>
        public static IQueryBuilder<T> GreaterThan<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P value)
        {
            if (value != null)
            {
                var parameter = property.GetParameters();
                var constant = Expression.Constant(value);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                //如果是Nullable<X>类型，则转化成X类型
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                var methodExp = Expression.GreaterThan(nonNullProperty, constant);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter);
                q.Expression = q.Expression.And(lambda);
            }
            return q;
        }
        /// <summary>
        /// 建立 In 查询条件 add lamda
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="valuse">查询值</param> 
        /// <returns></returns>
        public static IQueryBuilder<T> In<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, params P[] values)
        {
            if (values != null && values.Length > 0)
            {
                var parameter = property.GetParameters();
                var constant = Expression.Constant(values);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                //如果是Nullable<X>类型，则转化成X类型
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                Expression<Func<P[], P, bool>> InExpression = (list, el) => list.Contains(el);
                var methodExp = InExpression;
                var invoke = Expression.Invoke(methodExp, constant, property.Body);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(invoke, parameter);
                q.Expression = q.Expression.And(lambda);
            }
            return q;
        }

        /// <summary>
        /// 建立 In 查询条件 or
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="q">动态查询条件创建者</param>
        /// <param name="property">属性</param>
        /// <param name="valuse">查询值</param> 
        /// <returns></returns>
        public static IQueryBuilder<T> OrIn<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, params P[] values)
        {
            if (values != null && values.Length > 0)
            {
                var parameter = property.GetParameters();
                var constant = Expression.Constant(values);
                Type type = typeof(P);
                Expression nonNullProperty = property.Body;
                //如果是Nullable<X>类型，则转化成X类型
                if (IsNullableType(type))
                {
                    type = GetNonNullableType(type);
                    nonNullProperty = Expression.Convert(property.Body, type);
                }
                Expression<Func<P[], P, bool>> InExpression = (list, el) => list.Contains(el);
                var methodExp = InExpression;
                var invoke = Expression.Invoke(methodExp, constant, property.Body);
                Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(invoke, parameter);
                q.Expression = q.Expression.Or(lambda);
            }
            return q;
        }
        ///// <summary>
        ///// 建立 Equals ( 相等 ) 查询条件
        ///// </summary>
        ///// <typeparam name="T">实体</typeparam>
        ///// <param name="q">动态查询条件创建者</param>
        ///// <param name="property">属性</param>
        ///// <param name="value">查询值</param>
        ///// <returns></returns>
        //public static IQueryBuilder<T> AttrEquals<T, P>(this IQueryBuilder<T> q, Expression<Func<T, P>> property, P value)
        //{
        //    if (value != null)
        //    {
        //        string[] attr = value.ToString().Split(';');
        //        foreach (var str in attr)
        //        {
        //            string[] val = str.Split(':');
        //            q.Expression=q.Expression.Equals(
        //        }

        //    }
        //    return q;
        //}


        private static ParameterExpression[] GetParameters<T, S>(this Expression<Func<T, S>> expr)
        {
            return expr.Parameters.ToArray();
        }
        /// <summary>
        /// 动态排序
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyStr"></param>
        /// <returns></returns>
        public static IQueryable<TEntity> myorder<TEntity>(this IQueryable<TEntity> source, string propertyStr) where TEntity : class
        {
            if (!string.IsNullOrEmpty(propertyStr))
            {
                ParameterExpression param = Expression.Parameter(typeof(TEntity), "c");
                string[] ps = propertyStr.Split(' ');
                string tempstr = propertyStr;
                string orderstr = "OrderBy";
                if (ps.Count() > 1)
                {
                    tempstr = ps[0];
                    if (ps[1].ToLower() == "desc")
                    {
                        orderstr = "OrderByDescending";
                    }
                }
                PropertyInfo property = typeof(TEntity).GetProperty(tempstr);

                Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);

                LambdaExpression le = Expression.Lambda(propertyAccessExpression, param);

                Type type = typeof(TEntity);

                MethodCallExpression resultExp = Expression.Call(typeof(Queryable), orderstr, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(le));

                return source.Provider.CreateQuery<TEntity>(resultExp);
            }
            else
            {
                return null;
            }

        }

        static bool IsNullableType(Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }

        static Type GetNonNullableType(Type type)
        {
            return type.GetGenericArguments()[0];
            //return IsNullableType(type) ? type.GetGenericArguments()[0] : type;
        }

    }
}
